1、内核中怎么表示一个输入设备
	内核中使用 struct input_dev 对象来表示输入设备，它的内容如下：
	struct input_dev {
		const char *name;
		const char *phys;
		const char *uniq;
		struct input_id id;
	
		//设备属性位图
		unsigned long propbit[BITS_TO_LONGS(INPUT_PROP_CNT)];
	
		//输入设备支持的事件类型位图
		unsigned long evbit[BITS_TO_LONGS(EV_CNT)];
		//相应事件可以上报的事件码位图
		//按键事件
		unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];
		//相对坐标事件
		unsigned long relbit[BITS_TO_LONGS(REL_CNT)];
		//绝对准备事件
		unsigned long absbit[BITS_TO_LONGS(ABS_CNT)];
		//其他事件
		unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)];
		//led事件
		unsigned long ledbit[BITS_TO_LONGS(LED_CNT)];
		//声音事件
		unsigned long sndbit[BITS_TO_LONGS(SND_CNT)];
		//压力反馈事件
		unsigned long ffbit[BITS_TO_LONGS(FF_CNT)];
		//压力状态事件
		unsigned long swbit[BITS_TO_LONGS(SW_CNT)];
		......
2、 APP 可以得到什么数据
	通过read可以得到一系列的输入事件，就是一个一个“struct input_event”（这部分功能由通用事件处理程序实现），
它定义如下：
	struct timeval {
		__kernel_time_t tv_sec;
		__kernel_suseconds_ tv_usec;
	};
	struct input_event {
		//事件上报时间
		struct timeval time;
		//表示哪类事件，比如 EV_KEY 表示按键类、EV_REL 表示相对位移，EV_ABS 表示绝对位置
		__u16 type;
		//表示该类事件下的事件码，比如对于 EV_KEY 类事件它表示按键值，对于触摸屏它表示是 X 还是Y ，或是压力值
		__u16 code;
		//表示事件值，对于按键它表示按键状态，对于触摸屏，它表示坐标值 x 值或 y 值或压力值
		__s32 value;
	};
3、事件之间的界线
	APP 读取数据时，可以得到一个或多个数据，比如一个触摸屏的一个触点会上报 X 、Y 位置信息，还可能会上报压力值
， APP 可通过同步事件确定此次上报是否结束，对于同步事件其 type 、 code 、 value 三项都是 0
4、确定设备信息
	通过命令 cat /proc/bus/input/devices 可以查看输入设备的详细信息，其内容格式如下：
	//设备 ID
	I: Bus=0019 Vendor=0000 Product=0001 Version=0000
	//设备名称
	N: Name="Power Button"
	//系统层次结构中设备的物理路径
	P: Phys=LNXPWRBN/button/input0
	//位于 sys 文件系统的路径
	S: Sysfs=/devices/LNXSYSTM:00/LNXPWRBN:00/input/input0
	//设备的唯一标识码
	U: Uniq=
	//与输入设备关联的输入事件处理程序
	H: Handlers=kbd event0
	//设备属性
	B: PROP=0
	//设备支持的事件类型
	B: EV=3
	//可上报的按键位图
	B: KEY=10000000000000 0
	
	I: Bus=0011 Vendor=0001 Product=0001 Version=ab41
	N: Name="AT Translated Set 2 keyboard"
	P: Phys=isa0060/serio0/input0
	S: Sysfs=/devices/platform/i8042/serio0/input/input1
	U: Uniq=
	H: Handlers=sysrq kbd event1 leds
	B: PROP=0
	B: EV=120013
	B: KEY=402000000 3803078f800d001 feffffdfffefffff fffffffffffffffe
	//设备支持的其他事件
	B: MSC=10
	//设备上的指示灯
	B: LED=7
	......
5、使用命令读取数据
	调试输入设备时，可使用 hexdump 读取输入设备上报的数据，其数据格式如下：
	|序号 | |秒     | |微秒   | |type| |code| |value  |
	0000340 0000 0000 0000 0000 508b   62dd   0000 0000
	0000350 a54d 0009 0000 0000 0003   0000   9d3f 0000
	0000360 508b 62dd 0000 0000 a54d   0009   0000 0000
6、应用程序开发
	1）包含头文件 #include <linux/input.h>
	2）打开设备（输入系统支持阻塞、非阻塞、 IO 多路复用、异步通知等机制， IO 多路复用、异步通知建议采用非阻
塞方式打开，避免 IO 错误或连续读写时导致程序意外阻塞）
	3）通过 ioctl 获取设备信息，控制命令即参数参考内核中的 \drivers\input\evdev.c 中的 evdev_do_ioctl 函数
	4）通过 read 读取输入设备上报的事件，通过 write 发送事件给输入设备（如控制LED等）
	5）使用完成关闭输入设备
7、移植 tslib
	tslib 是一个开源的用于操作触摸屏的库，它可以简化触摸屏编程，同时也集成了有些触摸屏测试工具，其移植过程如下：
	1）基于 buildroot
		make menuconfig
		-> Target packages 
			-> Libraries 
				-> Hardware handling 
					-> [*] tslib
	2）基于源码
		a）解压 tslib ，并进入源码目录
			tar -vxf tslib-1.21.tar.xz
			cd tslib-1.21/
		b）配置 tslib
			#--host=arm-none-linux-gnueabihf编译器前缀
			#--prefix=/安装到目标机的目录
			./configure --host=arm-none-linux-gnueabihf --prefix=/
		c）编译 tslib
			make
		d）安装 tslib
			#会在当前目录下创建一个tmp目录，并将此目录作为目标机的跟目录进行安装
			make install DESTDIR=$PWD/tmp
		e）将安装后得到的头文件和库文件拷贝到工具链中（如果不基于 tslib 开发应用程序可以忽略此步骤）
			#/usr/local/arm/gcc-arm-9.2-2019.12-x86_64-arm-none-linux-gnueabihf/arm-none-linux-gnueabihf/lib
			#c/usr/include是工具链头文件路径
			cp ./tmp/include/* /usr/local/arm/gcc-arm-9.2-2019.12-x86_64-arm-none-linux-gnueabihf/arm-none-l
		inux-gnueabihf/libc/usr/include
			#/usr/local/arm/gcc-arm-9.2-2019.12-x86_64-arm-none-linux-gnueabihf/arm-none-linux-gnueabihf/lib
			#c/usr/lib是工具链库文件路径
			cp -d ./tmp/lib/*so* /usr/local/arm/gcc-arm-9.2-2019.12-x86_64-arm-none-linux-gnueabihf/arm-none
		-linux-gnueabihf/libc/usr/lib
		f）将安装后得到的文件拷贝到目标板跟文件系统中
			#../../../../rootfs/是nfs根文件系统目录
			cp -r tmp/* ../../../../rootfs/
8、 tslib编程
	1）使用 ts_setup 打开触摸屏设备，并初始化 tslib
	2）使用 ioctl(int ts_fd(struct tsdev *ts), EVIOCGABS(ABS_MT_SLOT), struct input_absinfo *slot) 获取触摸
屏的 slot 信息，并计算触摸点数
	3）使用 ts_read_mt 读取触摸屏数据
	4）最后通过 ts_close 关闭触摸屏设备
9、 tslib 常用函数
	/* 打开触摸屏设备，并初始化 tslib
	 * dev_name 触摸设备名称，为NULL则会在系统中搜索
	 * nonblock 是否阻塞
	 * 成功返回tslib设备句柄
	 **/
	struct tsdev *ts_setup(const char *dev_name, int nonblock)
	/* 读取多点触摸屏数据
	 * ts tslib设备句柄
	 * samp 指向一个struct ts_sample_mt *对象，用于返回触摸坐标
	 * nr 表示采样次数，采样值依次存入samp[0][0] samp[0][1] ... samp[0][max_slots] samp[1][0] samp[1][1] ...
	 *    samp[1][max_slots] ... 中
	 * 成功返回实际采样次数
	 **/
	int ts_read_mt(struct tsdev *ts, struct ts_sample_mt **samp, int max_slots, int nr)
	/* 读取单点触摸屏数据
	 * ts tslib设备句柄
	 * samp：指向一个 struct ts_sample 对象，用于返回触摸坐标
	 *  nr ：表示采样次数。
	 * 成功返回实际采样次数
	 **/
	int ts_read(struct tsdev *ts, struct ts_sample *samp, int nr)
	/* 关闭触摸屏设备，并释放tslib资源
	 * ts tslib设备句柄
	 * 成功返回0
	 **/
	int ts_close(struct tsdev *ts)
	//ts_sample结构体
	struct ts_sample {
		//X 坐标
		int x;
		//Y 坐标
		int y;
		//按压力大小
		unsigned int pressure;
		//时间
		struct timeval tv;
	};
	//ts_sample_mt结构体
	struct ts_sample_mt {
		//X 坐标
		int x; 
		//Y 坐标
		int y; 
		//按压力大小
		unsigned int pressure;
		//触摸点 slot
		int slot;
		//ID
		int tracking_id;
		int tool_type;
		int tool_x;
		int tool_y;
		unsigned int touch_major;
		unsigned int width_major;
		unsigned int touch_minor;
		unsigned int width_minor;
		int orientation;
		int distance;
		int blob_id;
		//时间
		struct timeval tv;
		//BTN_TOUCH 的状态
		short pen_down;
		//此次样本是否有效标志 触摸点数据是否发生更新
		short valid;
	};